home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 001 / xmodem.arc / XMDM2.C < prev    next >
Text File  |  1985-06-15  |  14KB  |  496 lines

  1. /*---------------------------------------------------------------
  2.                             XMODEM.C
  3.                     By Donald G. Krantz 4/84
  4.                (C) Copyright 1984 Donald G. Krantz
  5.                        All Rights Reserved
  6.  
  7.             Christensen Protocol Routines for XMODEM.C
  8. This file contains a stand-alone Christensen Engine, which
  9. requires the following interface to another system:
  10.  
  11. error() is called to terminate a transfer: the parameter is a 
  12. string describing the error condition.
  13. VOID error( str )
  14.    char *str;
  15.  
  16. abort() is called at strategic places to determine if the 
  17. local operator wants to quit sending/receiving.
  18. VOID abort()
  19.  
  20. rx() picks up a character from the remote.
  21. char rx()
  22.  
  23. rxstat() returns TRUE when a character is ready from the
  24. remote.
  25. int rxstat()
  26.  
  27. tx() sends a character to the remote.
  28. VOID tx( ch )
  29.    char ch;
  30.  
  31. lcl_str() sends an informational message to the local
  32. console.
  33. lcl_str( str )
  34.    char *str;
  35.  
  36. ---------------------------------------------------------------*/
  37.  
  38. #include "stdio.h"        /* ECO C */
  39. #include "b:xmodem.h"
  40.  
  41. /*---------------------------------------------------------------
  42. txfile() does transmission of a file.
  43. Calling sequence: "fd" is a file pointer to the open file
  44. to be transmitted.
  45.  
  46. References globals: buffer;
  47. Modifies globals: rec, crc;
  48. ---------------------------------------------------------------*/
  49. VOID txfile( fd )
  50.    FILE *fd;
  51. {
  52.    char ch;            /* scratch character        */
  53.    char *i;            /* buffer pointer (index)    */
  54.    int y;            /* scratch counter        */
  55.  
  56.     rec = 1;        /* uses "natural" numbering    */
  57.     while( rxstat() )
  58.         rx();
  59.     while( TRUE )        /* handshake w/ receiver    */
  60.     {
  61.         if( (ch = wait( 10, TRUE, TRUE )) == NAK )
  62.         {
  63.             crc = FALSE;
  64.             break;
  65.         }
  66.         if( ch == CRC )
  67.         {
  68.             crc = TRUE;
  69.             break;
  70.         }
  71.         abort();
  72.     }
  73.     while( fillbuf( fd, buffer ) )    /* file not empty?    */
  74.     {
  75.         txrec( buffer );    /* send record        */
  76.         abort();
  77.     }
  78.     tx( EOT );
  79.     while( (ch = wait( 1, TRUE, TRUE )) != ACK )
  80.     {
  81.         tx( EOT );        /* show end        */
  82.     }
  83.     fclose( fd );            /* dump file        */
  84. }
  85.  
  86. /*---------------------------------------------------------------
  87. txname() transmits a file name from the CP/M fcb format 
  88. parameter 'name'.
  89.  
  90. References globals: buffer, checksum, crc;
  91. Modifies globals: crc;
  92. ---------------------------------------------------------------*/
  93. VOID txname( name )
  94.    char *name;
  95. {
  96.    register int i;        /* scratch counter        */
  97.    char ch;            /* scratch char            */
  98.    char crcsav;            /* holds state of global 'crc'    */
  99.  
  100.     crcsav = crc;        /* we always use checksum for    */
  101.     crc = FALSE;        /* name error detection        */
  102.     while( TRUE )        /* main loop for retries    */
  103.     {
  104.         while( rxstat() )
  105.             rx();
  106.         i = 0;        /* retry count            */
  107.         while( TRUE )
  108.         {
  109.             if( wait( RETRY, TRUE, TRUE ) == NAK )
  110.                 break;
  111.             if( i++ > RETRY )
  112.                 error( "Can't send filename" );
  113.         }
  114.         tx( ACK );    /* handshake name request    */
  115.         sleep();    /* decent interval        */
  116.         clrcrc();    /* clear checksum accumulator    */
  117.         for( i = 0 ; i < NAMESIZE ; i++ )/* name loop    */
  118.         {
  119.             tx( name[ i ] );
  120.             wait( 10, TRUE, TRUE );/* wait for ACK    */
  121.         }
  122.         tx( EoF );        /* name terminated w/EoF*/
  123.         if(wait(10,FALSE,TRUE)==checksum) /* handshake    */
  124.         {
  125.             tx( ACK );    /* handshake OK checksum*/
  126.             crc = crcsav;    /* replacce 'crc'    */
  127.             return;        /* normal exit        */
  128.         }
  129.         tx( BADNAME );        /* handshake bad chksm    */
  130.     }
  131. }
  132.  
  133. /*---------------------------------------------------------------
  134. txrec() transmits a single record, with retransmit on error from
  135. receiver. Input is a pointer to the I/O buffer.
  136.  
  137. References globals: rec, crcaccum, checsum, crc;
  138. Modifies globals: rec;
  139. ---------------------------------------------------------------*/
  140. VOID txrec( buf )
  141.    char *buf;
  142. {
  143.    register int i;
  144.    unsigned cr;
  145.  
  146.     while( TRUE )            /* do it until right    */
  147.     {
  148.         sprintf( msg, "\rTransmitting record %d   ", rec );
  149.         lcl_str( msg );
  150.         tx( SOH );        /* start of header    */
  151.         tx( rec );        /* rec #        */
  152.         tx( ~rec );        /* 1's comp        */
  153.         clrcrc();        /* clear CRC accum    */
  154.         for( i = 0 ; i < RECSIZE ; i++ )
  155.             tx( buf[ i ] );    /* send record        */ 
  156.         updcrc( 0 );        /* finish up CRC    */
  157.         updcrc( 0 );        /* again        */
  158.         cr = crcaccum;         /* save crc lobyte    */
  159.         if( crc )        /* send hi byte first    */
  160.         {
  161.             tx( crcaccum >> 8 );
  162.             tx( cr );
  163.         }
  164.         else
  165.             tx( checksum );
  166.         if( wait(10, TRUE, TRUE)==ACK)/* quit if correct*/
  167.             break;        
  168.     }
  169.     rec++;                /* bump record count    */
  170. }
  171.  
  172. /*---------------------------------------------------------------
  173. rxname() loads a CP/M style fcb with a filename from remote
  174. sender. On receipt of an EOT instead of a name character, exits
  175. ---------------------------------------------------------------*/
  176. VOID rxname( fcb )
  177.    char *fcb;            /* points to CP/M style fcb    */
  178. {
  179.    char *fcbptr;        /* index to fcb            */
  180.    register int i;        /* scratch counter        */
  181.    int ch;            /* scratch char    (must hold ERR)    */
  182.    char chksum;            /* checksum accumulator        */
  183.  
  184.     while( TRUE )
  185.     {
  186.         fcbptr = fcb;        /* align index        */
  187.         i = RETRY * 5;        /* retry for name h.s.    */
  188.         while( TRUE )        /* Handshake NAK    */
  189.         {
  190.             abort();    /* check operator abort    */
  191.             tx( NAK );    
  192.             if( wait( 1, FALSE, FALSE) == ACK )
  193.                 break;    
  194.             if( !( i-- ) )
  195.                 error( "Timed out waiting for name" );
  196.         }
  197.         chksum = EoF;        /* init checksum    */
  198.         for( i = 0 ; i < 34 ; i++ )    /* accept noise    */
  199.         {
  200.             if( (ch = wait(1,FALSE,FALSE)) == EOT )
  201.             {
  202.                 tx( ACK );
  203.                 exit( 0 );
  204.             }
  205.             if( ch == EoF )    /* End of name chars    */
  206.                 break;
  207.             if( ch != ERROR )    /* not timeout    */
  208.             {
  209.                 *(fcbptr++) = ch & 0x7F;
  210.                 chksum += ch & 0x7F;
  211.             }
  212.             abort();    /* operator abort    */
  213.             tx( ACK );    /* handshake name char    */
  214.         }
  215.         fcb[ NAMESIZE ] = 0;    /* terminate name field    */
  216.         do {
  217.             abort();    /* operator abort    */
  218.             tx( chksum );    /* handshake checksum    */
  219.         } while( (ch = wait(1,FALSE,FALSE)) == ERROR );
  220.         if( ch == ACK )        /* ACK is good name    */
  221.             return;
  222.     }
  223. }
  224.     
  225. /*---------------------------------------------------------------
  226. rxfile() receives a file. Input parameter is a file pointer 
  227. to the local file receiving the transmitted file.
  228.  
  229. References globals: buffer, rec, crcaccum, checksum, crc;
  230. Modifies globals: buffer, rec;
  231. ---------------------------------------------------------------*/
  232. VOID rxfile( fd )
  233.    FILE *fd;
  234. {
  235.    char ch;            /* scratch handshake var    */
  236.    char response;        /* ACK/NAK/CRC handshake    */
  237.    char crcrlo;            /* rec'd CRC low byte        */
  238.    char crcrhi;            /* rec'd CRC hi byte        */
  239.    char r1;            /* current record number    */
  240.    char r2;            /* 1's comp record number    */
  241.    unsigned int j;        /* wait loop timer        */
  242.    int i;            /* scratch counter        */
  243.    register char *bptr;        /* buffer index            */
  244.  
  245.     while( rxstat() )
  246.         rx();
  247.     rec = 1;        /* uses natural numbering    */
  248.     if( crc )        /* set initial handshake to    */
  249.         response = CRC;    /* CRC or checksum        */
  250.     else
  251.         response = NAK;
  252.     while( TRUE )        /* record receive loop        */
  253.     {
  254.         bptr = buffer;        /* align index        */
  255.         sprintf( msg, "\rWaiting for record %d    ", rec );
  256.         lcl_str( msg );
  257.         for( i=1 ; i <= RETRY * 5 ; i++ )
  258.         {
  259.             abort();
  260.             tx( response );    /* send handshake    */
  261.             if( (ch = wait(1,TRUE,TRUE)) == SOH )
  262.                 break;    /* SOH indicatees rec    */
  263.             if( ch == EOT )    /* EOT indicates done    */
  264.             {
  265.                 fclose( fd );
  266.                 tx( ACK );    /* handshake     */
  267.                 return;        /* normal exit    */
  268.             }
  269.             if( ch == CAN )    /* Xmit request abort    */
  270.             {
  271.                 fclose( fd );
  272.                 error("\rReceived cancel request");
  273.             }
  274.             if( i == RETRY * 5 )    /* timeout exit    */
  275.                 error( "Can't sync to sender" );
  276.         }
  277.         r1 = wait(1,FALSE,FALSE); /* record number    */
  278.         r2 = wait(1,FALSE,FALSE); /* 1's comp record #    */
  279.         while( bptr - buffer < RECSIZE ) /* test count    */
  280.         {
  281.             *(bptr++)=wait(1,FALSE,FALSE);/*accept char*/
  282.         }
  283.         if( crc )        /* get hibyte CRC    */
  284.             crcrhi = wait(1,FALSE,FALSE);
  285.         crcrlo = wait(1,FALSE,FALSE);/* lobyte CRC or chksm*/
  286.         response = NAK;        /* init response    */
  287.         if( (~r1 & 0xFF) != (r2 & 0xFF) )
  288.             continue;
  289.         clrcrc();        /* calc checksum/CRC    */
  290.         for( j = 0 ; j < RECSIZE ; j++ )
  291.             updcrc( buffer[ j ] );
  292.         updcrc( 0 );        /* required to finish    */
  293.         updcrc( 0 );        /* off CRC - why?    */
  294.         if( crc )        /* CRC test        */
  295.             if( (crcrlo + (crcrhi << 8)) != crcaccum )
  296.                 continue;
  297.         if( !crc )        /* checksum test    */
  298.             if( crcrlo != checksum )
  299.                 continue;
  300.         if( (r1 == (rec - 1) & 0xFF ) )    /* duplicate?    */
  301.         {
  302.             response = ACK;    /* dup is OK - ACK was    */
  303.             continue;    /* trashed - ignore it    */
  304.         }
  305.         if( r1 != (rec & 0xFF) )/* fatal sequence error    */
  306.             error( "File record numbering error" );
  307.         rec++;            /* bump record count    */
  308.         for( j = 0 ; j < RECSIZE ; j++ ) /* write data    */    
  309.             putc( buffer[ j ], fd );
  310.         response = ACK;        /* normal loop end    */
  311.     }
  312. }
  313.  
  314. /*---------------------------------------------------------------
  315. parse() expands  non - ambiguous filespecs to the
  316. standard CP/M fcb format, excluding drive byte. 
  317. Inputs are "normal" filespec (inspec), and exanded fcb.
  318. ---------------------------------------------------------------*/
  319. char *parse( inspec, fcb )
  320.    char *inspec, *fcb;
  321. {
  322.    register int i;            /* fcb index        */
  323.    int inptr;                /* input spec index    */
  324.  
  325.     for( i = 0 ; i < NAMESIZE ; i++ )/* blank fill name    */
  326.         fcb[ i ] = ' ';
  327.     if( inspec[ 1 ] == ':' )    /* check for drivspec    */
  328.         inptr = 2;        /* point past drivespec    */
  329.     else
  330.         inptr = 0;        /* index to start    */
  331.     i = 0;                /* pointer into fcb    */
  332.     while( TRUE )
  333.         switch( inspec[ inptr++ ] )
  334.         {
  335.             case '\0':    /* end of input spec    */
  336.                 fcb[ NAMESIZE ] = 0;
  337.                 return( fcb );
  338.             case '.':    /* extension spec'ed    */
  339.                 i = NAMESIZE - 3; /* extension    */
  340.                 break;
  341.             default:
  342.                 if( i < NAMESIZE )
  343.                     fcb[ i++ ] = toupper( 
  344.                     inspec[ inptr - 1 ] );
  345.         }
  346. }
  347.  
  348. /*---------------------------------------------------------------
  349. unparse() reassembles a filename from a CP/M fcb into "normal" or
  350. cmpressed form, so that our C functions can deal with them.
  351. Inputs are a pointer to a string to receive the name (name), and
  352. a pointer to a CP/M style fcb entry (buf).
  353. ---------------------------------------------------------------*/
  354. char *unparse( name, buf )
  355.    char *name, *buf;
  356. {
  357.    register int i;        /* 'name' index            */
  358.    int j;            /* 'buf' index            */
  359.  
  360.     i = 0;            /* 'driveless' name    */
  361.     for( j = 0 ; j < NAMESIZE ; j++ )/* transfer chars    */
  362.     {
  363.         if( buf[ j ] != ' ' )    /* (skip spaces)    */
  364.             name[ i++ ] = buf[ j ] & 0x7F;
  365.         if( j == NAMESIZE-4 )    /* don't forget dot    */
  366.             name[ i++ ] = '.';
  367.     }
  368.     name[ i ] = '\0';        /* terminate string    */
  369.                     /* eat terminal dot    */
  370.     if( *(index( name, '.' ) + 1) == '\0' )
  371.         *(index( name, '.' )) = '\0'; 
  372.     return( name );            /* return pointer    */
  373. }
  374.  
  375. /*---------------------------------------------------------------
  376. fillbuf() loads the I/O buffer with a record from the input file.
  377. Returns TRUE if data was available, FALSE if no data left.
  378. ---------------------------------------------------------------*/
  379. int fillbuf( fd, buffer )
  380.    FILE *fd;
  381.    char *buffer;
  382. {
  383.    register int i;        /* scratch counter        */
  384.    int errorchk;        /* holds EOF in Eco C        */
  385.  
  386.     for( i = 0 ; i < RECSIZE ; i++ )
  387.     {
  388.         if( (errorchk = getc( fd)) == EOF )
  389.             break;
  390.         buffer[ i ] = errorchk;
  391.     }
  392.     if( i == 0 )            /* no data read        */
  393.         return( FALSE );
  394.     for( ; i < RECSIZE ; i++ )
  395.         buffer[ i ] = 0;    /* zero fill at EOF    */
  396.     return( TRUE );
  397. }
  398.  
  399. /*---------------------------------------------------------------
  400. clrcrc() clears the crc accumulator. Not much to it, actually.
  401.  
  402. References globals: 
  403. Modifies globals: crcaccum, checksum;
  404. ---------------------------------------------------------------*/
  405. VOID clrcrc()
  406. {
  407.     crcaccum = 
  408.     checksum = 0;
  409. }
  410.  
  411. /*---------------------------------------------------------------
  412. updcrc() updates the crc accumulator, if 'crc' is TRUE, else
  413. updates the checksum. 
  414. 'x' is the byte to be added to CRC or checksum.
  415. CCITT polynomial.
  416.  
  417. References globals: crc;
  418. Modifies globals: crcaccum, checksum;
  419. ---------------------------------------------------------------*/
  420. VOID updcrc( x )
  421.    char x;
  422. {
  423.    unsigned shifter, i, flag;
  424.  
  425.     if( crc )
  426.     {
  427.         for( shifter = 0x80 ; shifter ; shifter >>= 1 )
  428.         {
  429.             flag = (crcaccum & 0x8000);
  430.             crcaccum <<= 1;
  431.             crcaccum |= ((shifter & x) ? 1 : 0);
  432.             if( flag )
  433.                 crcaccum ^= 0x1021;
  434.         }
  435.     }
  436.     else
  437.         checksum += x;
  438. }
  439.  
  440. /*---------------------------------------------------------------
  441. sleep() does a short delay to account for transmission line
  442. latency, etc.
  443. ---------------------------------------------------------------*/
  444. VOID sleep()
  445. {
  446.    register unsigned int i;
  447.  
  448.     for( i=0 ; i < MAGIC_NUMBER ; i++ )
  449.         ;
  450. }
  451.  
  452. /*---------------------------------------------------------------
  453. wait() waits for a character - timeout built in. Timeout 
  454. condition causes error return to menu.
  455.   'time' controls duration of wait.
  456.   'is_cmd' TRUE indicates that CAN recvd will cause program exit
  457.   'error_out' TRUE indicates timeout causes program exit
  458. ---------------------------------------------------------------*/
  459. int wait( time, is_cmd, error_out )
  460.    int time;
  461. {
  462.    register unsigned int i;        /* loop timer        */
  463.    int j;                /* timeout count    */
  464.    int ch;
  465.  
  466.     j =                /* timeout count    */
  467.     i = 0;                /* inner timeout count    */
  468.     while( !rxstat() )
  469.         if( i++ > MAGIC_NUMBER )/* about 1.5 secs    */
  470.             if( !(time--) ) /* check retry count    */
  471.                 if( error_out )
  472.                     error( "Receiver timed out" );
  473.                 else
  474.                     return( ERROR );
  475.             else        /* more tries available    */
  476.             {
  477.                 abort(); /* scan for ^X        */
  478.                 if( !j ) /* \n first timeout    */
  479.                 {
  480.                     sprintf( msg, "\n" );
  481.                     lcl_str( msg );
  482.                 }
  483.                 sprintf( msg, "\rtimeout %d   ", ++j );
  484.                 lcl_str( msg );
  485.                 i = 0;
  486.             }
  487.     if( !is_cmd )    
  488.         return( rx() );        /* send back char    */
  489.     if( (ch = rx()) == CAN )
  490.         error( "Received cancellation request" );
  491.     return( ch );
  492. }
  493.  
  494. n( rx() );        /* send back char    */
  495.     if( (ch = rx()) == CAN )
  496.         error